import {BaseGameEntity as Entity} from "../game/BaseGameEntity";
import {State} from "./State";
import {Telegram} from "../message/Telegram";


export class StateMeachine<T extends Entity> {

    //a pointer to the agent that owns this instance
    private _owner: T;

    private _currentState:State<T> = null;

      //a record of the last state the agent was in
    private _previousState:State<T> = null;

    //this is called every time the FSM is updated
    private _globalState:State<T> = null;

    get currentState():State<T> {
        return this._currentState;
    }

    get previousState():State<T> {
        return this._previousState;
    }

    get globalState():State<T> {
        return this._globalState;
    }

    constructor(owner:T) {
        this._owner = owner;
    }

    //use these methods to initialize the FSM
    setCurrentState(s:State<T>): void {
        this._currentState = s;
    }
    setPreviousState(s:State<T>): void {
        this._previousState = s;
    }
    setGlobalState(s:State<T>): void {
        this._globalState = s;
    }

    update(): void {
        //if a global state exists, call its execute method, else do nothing
        if(this._globalState)
            this._globalState.execute(this._owner);

        //same for the current state
        if(this._currentState)
            this._currentState.execute(this._owner);
    }

    handlerMessage(t:Telegram):boolean {
        //first see if the current state is valid and that it can handle
        //the message
        if(this._currentState && this._currentState.onMessage(this._owner, t)) {
            return true;
        }

        //if not, and if a global state has been implemented, send 
        //the message to the global state
        if(this._globalState && this._globalState.onMessage(this._owner, t)) {
            return true;
        }

        return false;
    }

    //change to a new state
    changeState(newState:State<T>): void {

        //keep a record of the previous state
        this._previousState = this._currentState;

        //call the exit method of the existing state
        this._currentState.exit(this._owner);

        //change state to the new state
        this._currentState = newState;

        //call the entry method of the new state
        this._currentState.enter(this._owner);
    }

    //change state back to the previous state
    revertToPreviousState(): void {
        this.changeState(this._previousState);
    }

    //returns true if the current state's type is equal to the type of the
    //class passed as a parameter. 
    isInState(st:State<T>): boolean {
        return typeof this._currentState === typeof st;
    }
}